package com.jweb.framework.http.client;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.params.CookiePolicy;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.params.ConnPerRouteBean;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.cookie.Cookie;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.apache.http.message.BasicHeader;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;

import com.alibaba.fastjson.JSON;
import com.jweb.framework.core.CoreRuntimeException;
import com.jweb.framework.core.channel.CommunicationException;
import com.jweb.framework.core.channel.Gateway;

/** 
 * @author Thomas: 
 * 类说明 ：APP端模拟PC登录
 */
public class HttpFormSecurityGateway implements Gateway , InitializingBean {
	
	protected Logger logger = LoggerFactory.getLogger(HttpFormSecurityGateway.class);
	
	private HttpClient httpClient ; 
	private int maxTotalConnections = 4 ; 
	private int maxConnectionsPerRoute = 2 ; //
	
	private String methodName = "POST";
	private String encoding = "UTF-8";
	private String url ;
	
	private int connectionTimeout = 120*1000;
	private int soTimeout = 150*1000 ;
	
	private Properties httpUrl ;
	private BasicCookieStore basicCookieStore;
	
	public void setConnectionTimeout(int connectionTimeout) {
		this.connectionTimeout = connectionTimeout;
	}

	public void setSoTimeout(int soTimeout) {
		this.soTimeout = soTimeout;
	}
	
	public void setHttpClient(HttpClient httpClient) {
		this.httpClient = httpClient;
	}

	public void setMethodName(String methodName) {
		this.methodName = methodName;
	}

	public void setUrl(String url) {
		this.url = url;
	}
	
	public void setHttpUrl(Properties pros){
		this.httpUrl = pros ;
	}

	public void setEncoding(String enc){
		if("UTF-8".equals(enc)){
			enc = "UTF-8";
		}
		else if("UTF-16".equals(enc)){
			enc = "UTF-16";
		}
		else if("US-ASCII".equals(enc)){
			enc = "US-ASCII";
		}
		else if("ASCII".equals(enc)){
			enc = "ASCII";
		}
		else if("ISO-8859-1".equals(enc)){
			enc = "ISO-8859-1";
		}
		else {
			throw new IllegalArgumentException("unsupported encoding , supported: UTF-8, UTF-16, US-ASCII, ASCII, ISO-8859-1.");
		}
		
		this.encoding = enc ;
	}
	
	public void setMaxTotalConnections(int mtc){
		this.maxTotalConnections = mtc ;
	}
	
	public void setMaxConnectionsPerRoute(int mcpr){
		this.maxConnectionsPerRoute = mcpr ;
	}
	
	public void afterPropertiesSet() throws Exception {
		BasicHttpParams basicHttpParams = new BasicHttpParams();
		
		ConnManagerParams.setMaxTotalConnections(basicHttpParams ,  this.maxTotalConnections);
		ConnPerRouteBean connPerRouteBean = new ConnPerRouteBean(this.maxConnectionsPerRoute);
		ConnManagerParams.setMaxConnectionsPerRoute(basicHttpParams , connPerRouteBean);
		
		HttpProtocolParams.setVersion(basicHttpParams , HttpVersion.HTTP_1_1);
		HttpConnectionParams.setLinger(basicHttpParams , 0);
		
		SchemeRegistry schemeRegistry = new SchemeRegistry();
		schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory() , 80));
		
		ThreadSafeClientConnManager threadSafeClientConnManager = new ThreadSafeClientConnManager(basicHttpParams , schemeRegistry);
		
		this.httpClient = new DefaultHttpClient(threadSafeClientConnManager , basicHttpParams);
		
		/**设置连接超时时间 和 读取超时时间*/
		HttpParams params = httpClient.getParams();
		HttpConnectionParams.setConnectionTimeout(params, this.connectionTimeout);
		HttpConnectionParams.setSoTimeout(params, this.soTimeout);
		
		basicCookieStore = new BasicCookieStore();
	}

	@Override
	public Object send(Object obj, String s) throws CommunicationException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Object receive(Object obj, String s) throws CommunicationException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Object sendAndReceive(Object obj, String urlKey)
			throws CommunicationException {
		
		HttpPost httpPost = null ;
		String url = null ;
		String sessionId = null ;
		
		if((this.httpUrl == null) || ("".equals(this.httpUrl))){
			url = urlKey ;
		}
		else if(urlKey.indexOf("@")>-1){
			String key = urlKey.split("@")[0];
			sessionId = urlKey.split("@")[1];
			url = this.httpUrl.getProperty(key) ;
			System.out.println(" ..................... url = " + url);
		}
		else {
			url = this.httpUrl.getProperty(urlKey.toString());
		}
		
		if((url == null) || (url == "")){
			throw new CoreRuntimeException("unsupported url request . ");
		}
		
		if(this.methodName.equalsIgnoreCase("POST")){
			httpPost = new HttpPost(url);
			httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
			
			if(sessionId != null){
				((DefaultHttpClient)this.httpClient).getCookieStore().clear();
				basicCookieStore.addCookie(new BasicClientCookie("KURRENT_REMEMBER_ME_COOKIE", sessionId));
				((DefaultHttpClient)this.httpClient).setCookieStore(this.basicCookieStore);
				this.httpClient.getParams().setParameter("http.protocol.cookie-policy", CookiePolicy.BROWSER_COMPATIBILITY);
				
				httpPost.setHeader(new BasicHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"));
				httpPost.setHeader(new BasicHeader("Accept-Encoding", "gzip, deflate, sdch"));
				httpPost.setHeader(new BasicHeader("Accept-Language", "zh-CN,zh;q=0.8"));
				httpPost.setHeader(new BasicHeader("Cache-Control", "no-cache"));
				httpPost.setHeader(new BasicHeader("Connection", "keep-alive"));
				httpPost.setHeader(new BasicHeader("Cookie", "JSESSIONID="+sessionId));
				httpPost.setHeader(new BasicHeader("Pragma", "no-cache"));
				httpPost.setHeader(new BasicHeader("Referer", url));
				httpPost.setHeader(new BasicHeader("Upgrade-Insecure-Requests", "1"));
				httpPost.setHeader(new BasicHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36"));
			}
			
			try {
				String requestForm = parseForm((Map)obj, url);
				
				logger.debug("请求URL及参数:" + requestForm);
				System.out.println("请求URL及参数:" + requestForm);
				
				httpPost.setEntity(new StringEntity(requestForm, this.encoding));
			} catch (UnsupportedEncodingException e) {
				throw new CoreRuntimeException("unsupported encoding, supported: UTF-8, UTF-16, US-ASCII, ASCII, ISO-8859-1.");
			}
		}
		
		HttpResponse httpResponse = null ;
		HttpEntity httpEntity = null ;
		
		try {
			
			httpResponse = this.httpClient.execute(httpPost);
			httpEntity = httpResponse.getEntity();
			
			CookieStore cookieStore = ((DefaultHttpClient )this.httpClient).getCookieStore();
			List<Cookie> cookies = cookieStore.getCookies();
			String sessionIdResp = cookies.get(0).getValue();
			logger.debug("获取SESSIONID=" + sessionIdResp);
			
			if(httpEntity != null){
				byte[] bytes = null ;
				try {
					bytes = EntityUtils.toByteArray(httpEntity);
				} catch (IOException ee) {
					throw new CommunicationException(false , ee);
				}
				
				Map<String, Object> resultMap = parseResultBytes(bytes) ;
				resultMap.put("SESSIONID", sessionIdResp);
				return resultMap ;
			}
			
			throw new CommunicationException(true , "no data found");
		} catch (ClientProtocolException e) {
			throw new CommunicationException(false , e);
		} catch (IOException ie) {
			throw new CommunicationException(true , ie);
		} finally {
			try {
				EntityUtils.consume(httpEntity);
			} catch (IOException ex) {
				ex.printStackTrace() ;
			}
		}
	}
	
	/**
	 * 解析返回报文
	 * @param bytes
	 * @return
	 */
	private Map<String, Object> parseResultBytes(byte[] bytes) {
		String str = null ;
			try {
				str = (this.encoding == null) ? new String(bytes) : new String(bytes , this.encoding) ;
			} catch (Exception e) {
				throw new CoreRuntimeException("JAVAHT0031", "unsupport_encoding", e);
			}
		
		if((str!=null) && (str.trim().equals(""))){
			return new HashMap();
		}
		
		return JSON.parseObject(str , Map.class);
	}

	/**
	 * 对请求报文进行封装
	 * @param obj
	 * @return
	 */
	private String parseForm(Map map, String url) {
		StringBuffer sb = new StringBuffer();
		for(Iterator it = map.entrySet().iterator(); it.hasNext(); ){
			Entry entry = (Entry) it.next();
			String key = (String) entry.getKey();
			String value = (String) entry.getValue();
			sb.append(key).append("=").append(value).append("&");
		}
		
		StringBuffer req = new StringBuffer(url);
		req.append("?").append(sb);
		
		String requestStr = null ;
		if(!map.isEmpty()){
			requestStr = req.substring(0, req.length()-1);
		}
		
		return requestStr ;
	}

}
